tree grammar TarrhTreeWalker;

options {
    tokenVocab=Tarrh;                    // import tokens from Calc.tokens
    ASTLabelType=CommonTree;            // AST nodes are of type CommonTree
}

@header{
	package nl.utwente.fmt.vertalerbouw.tarrh.treewalker;
	import org.apache.commons.lang.mutable.MutableInt;
}

@rulecatch { 
    catch (RecognitionException e) { 
        throw e; 
    } 
}

@members {
	public TarrhTreeWalker(TreeWalker treewalker, TreeNodeStream input) {
		this(input);
		this.treewalker = treewalker;
	}
	
 	private TreeWalker treewalker;
}


program			
		: ^(PROGRAM {treewalker.start();} (class_decl | function_decl)* main_function_decl {treewalker.end();})
		;

class_decl
		: ^(CLASS_DECL id=CLASS_OR_FUNCTION_IDENTIFIER {treewalker.classStart(id.getText());} (function_decl|member_variable_decl)* {treewalker.classEnd(id.getText());})
		;
		
function_decl
		: ^(FUNCTION_DECL rt=CLASS_OR_FUNCTION_IDENTIFIER id=CLASS_OR_FUNCTION_IDENTIFIER {treewalker.functionStart(rt.getText(), id.getText()); MutableInt position=new MutableInt();} 
			 argument_decl[position]*  {treewalker.functionBody(rt.getText(), id.getText());}
			statement*
			){treewalker.functionEnd(rt.getText(), id.getText());} 
		;
		
main_function_decl
		:^(MAIN_FUNCTION_DECL {treewalker.mainFunctionStart();} statement* {treewalker.mainFunctionEnd();})
		| MAIN_FUNCTION_DECL {treewalker.mainFunctionStart();treewalker.mainFunctionEnd();}
		;

argument_decl[MutableInt position]
		: ^(ARGUMENT_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER)
		{ {treewalker.functionArgument(position.intValue(), type.getText(), id.getText()); position.increment();} }
		;

member_variable_decl
		: ^(MEMBER_VAR_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER) 
		{   treewalker.declareMemberVariable(type.getText(), id.getText()); }
		| ^(MEMBER_VAR_ARRAY_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER)
		{   treewalker.declareMemberArray(type.getText(), id.getText()); }
		| ^(MEMBER_CONST_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER ex=expr)
		{   treewalker.declareMemberConstant(type.getText(), id.getText(), ex); }
		;

statement
		:	^(EXPR_STATEMENT ex=expr)
			{   treewalker.expressionStatement(ex); }
		|	^(PRINT (ex=expr))
			{   treewalker.print(ex); }
		|	^(READ var=VARIABLE_IDENTIFIER)
			{   treewalker.read(var.getText()); }
		|	variable_decl
		| 	if_statement
		| 	try_statement
		|	while_statement
		|	^(THROW ex=expr) 
		{treewalker.throwException(ex);	}
		;

variable_decl
		: ^(VAR_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER) 
		{   treewalker.declareVariable(type.getText(), id.getText()); }
		| ^(VAR_ARRAY_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER)
		{   treewalker.declareArray(type.getText(), id.getText()); }
		| ^(CONST_DECL type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER ex=expr)
		{   treewalker.declareConstant(type.getText(), id.getText(), ex); }
		;

while_statement
		: ^(WHILE {  Object obj=treewalker.whileStart(); } ex=expr {  treewalker.whileCondition(obj, ex); } statement* {   treewalker.whileEnd(obj, ex); })
		;


if_statement
		: ^(IF ex=expr { Object obj=treewalker.ifStart(ex); } statement*  ELSE {   treewalker.ifElse(obj, ex); } statement* {   treewalker.ifEnd(obj, ex); })
		;


try_statement
		:^(TRY {  Object obj=treewalker.tryStart(); } statement* {treewalker.tryEnd(obj); } CATCH type=CLASS_OR_FUNCTION_IDENTIFIER id=VARIABLE_IDENTIFIER {treewalker.tryCatchStart(obj, type.getText(), id.getText()); } statement*  {treewalker.tryCatchEnd(obj, type.getText(), id.getText()); })
		;

expr returns [Object result = null]
		: ex=expr1  {result = ex;}
		| ^(BECOMES ex=assignment_expression)
			 { result =ex; }
		;
		
assignment_expression returns [Object result = null]
		: var=VARIABLE_IDENTIFIER ex=expr {result=treewalker.assign(var.getText(), ex);}
		| ^(HOOKL var=VARIABLE_IDENTIFIER index=expr) ex=expr {result=treewalker.arrayIndexAssign(var.getText(), index, ex);}
		;

expr1 returns [Object result = null]
	: ex= expr2 {result = ex; }
	| ^(OR ex1=expr ex2=expr)
		{result =treewalker.operatorOr(ex1, ex2); }
	;

expr2 returns [Object result = null]
	:ex = expr3 {result = ex; }
	| ^(AND ex1=expr ex2=expr)
		{result =treewalker.operatorAnd(ex1, ex2); }
	;

expr3 returns [Object result = null]
	: ex= expr4	{result = ex;}
	| ^(EQUALS ex1=expr ex2=expr)
		{result =treewalker.operatorEquals(ex1, ex2); }
	| ^(NOTEQUALS ex1=expr ex2=expr)
		{result =treewalker.operatorNotEquals(ex1, ex2); }
	| ^(GREATER ex1=expr ex2=expr)
		{result =treewalker.operatorGreater(ex1, ex2); }
	| ^(LESS ex1=expr ex2=expr)
		{result =treewalker.operatorLess(ex1, ex2); }
	| ^(GREATEREQUALS ex1=expr ex2=expr)
		{result =treewalker.operatorGreaterEquals(ex1, ex2); }
	| ^(LESSEQUALS ex1=expr ex2=expr)
		{result =treewalker.operatorLessEquals(ex1, ex2); }
	;
    
expr4 returns [Object result = null]
    :  ex=expr5 {result = ex;}
    |   ^(PLUS ex1=expr ex2=expr) 
    	{result=treewalker.operatorPlus(ex1, ex2); }
    |   ^(MINUS ex1=expr ex2=expr)
    	{result=treewalker.operatorMinus(ex1, ex2); }
    ;

expr5 returns [Object result = null]
	: ex=expr6 {result = ex;}
	|	^(DIVISION ex1=expr ex2=expr)
		{result=treewalker.operatorDivision(ex1, ex2); }
	|	^(MULTI ex1=expr ex2=expr)
		{result=treewalker.operatorMultiply(ex1, ex2); }
	| 	^(MODULO ex1=expr ex2=expr)
		{result=treewalker.operatorModulo(ex1, ex2); }
	;

expr6  returns [Object result = null]
		: ex = operand {result = ex;}
		| ^(DOT	ex=expr id=CLASS_OR_FUNCTION_IDENTIFIER {Object obj=treewalker.functionCallStart(ex, id.getText()); MutableInt position = new MutableInt();} argument_call[obj, position]* {result=treewalker.functionCallEnd(obj, position.intValue(), ex, id.getText()); })
		| ^(HOOKL ex=expr index=expr {result=treewalker.arrayIndexAccess(ex,index);})
		;

operand returns [Object result = null]
   		: 	id=VARIABLE_IDENTIFIER
   			{result=treewalker.accessVariable(id.getText()); }
   		|   ^(INT_LITERAL min=MINUS? num=NUMBER)
   			{int n = Integer.parseInt(num.getText()); result=treewalker.literalInteger(min!=null?-n:n); }				
   		|   ^(STRING_LITERAL str=STRING)
   			{result=treewalker.literalString(str.getText().substring(1, str.getText().length()-1)); }		
   		|	BOOL_LITERAL_TRUE
   		{result=treewalker.literalBoolean(true); }
   		| BOOL_LITERAL_FALSE
   			{result=treewalker.literalBoolean(false); }
   		| ^(NEW_EXPR type=CLASS_OR_FUNCTION_IDENTIFIER)
			{result=treewalker.newObject(type.getText()); }
		| ^(NEW_ARRAY_EXPR type=CLASS_OR_FUNCTION_IDENTIFIER ex=expr)
			{result=treewalker.newArray(type.getText(), ex); }
		| ^(STATEMENT_EXPR {Object obj=treewalker.statementExpressionStart(); } statement* {result=treewalker.statementExpressionEnd(obj); })
		
		|	^(FUNCTION_CALL id=CLASS_OR_FUNCTION_IDENTIFIER {Object obj=treewalker.functionCallStart(id.getText()); MutableInt position = new MutableInt();}  argument_call[obj,position]* {result=treewalker.functionCallEnd(obj, position.intValue(), id.getText());})
   		;

		
argument_call[Object obj, MutableInt position]
		: ^(ARGUMENT_CALL ex=expr) {treewalker.functionCallArgument(obj, position.intValue(), ex); position.increment();}
		;
